package com.github.chirspan.xaas.security.config;

import cn.hutool.core.util.StrUtil;
import com.github.chirspan.xaas.security.component.ResourceAuthExceptionEntryPoint;
import com.github.chirspan.xaas.core.config.IgnoreUrlsProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import java.util.Arrays;
import java.util.Objects;

/**
 * @author ChenPan
 * @date: 2018/1/20
 * @description: OAuth2 资源服务器配置类
 */
/*@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)*/
public class SCResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private IgnoreUrlsProperties ignoreUrlsProperties;

    @Autowired(required = false)
    private RemoteTokenServices remoteTokenServices;

    @Autowired
    private OAuth2ClientProperties oAuth2ClientProperties;

    @Autowired
    private OAuth2WebSecurityExpressionHandler expressionHandler;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Autowired
    private AccessDeniedHandler oAuth2AccessDeniedHandler;

    @Autowired
    protected ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint;

    @Bean
    @Qualifier("authorizationHeaderRequestMatcher")
    public RequestMatcher authorizationHeaderRequestMatcher() {
        return new RequestHeaderRequestMatcher("Authorization");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 配置文件中配置的过滤路径，不需要登录

        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry =
                http
                        .csrf()
                        .disable()
                        .exceptionHandling()
                        .accessDeniedHandler(oAuth2AccessDeniedHandler)
                        .authenticationEntryPoint(resourceAuthExceptionEntryPoint)
                        .and()
                        .headers()
                        .frameOptions()
                        .disable()
                        .and()
                        .sessionManagement()
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                        .and()
                        .authorizeRequests()
                        // swagger start
                        .antMatchers("/swagger-ui.html",
                                "/swagger-resources/**",
                                "/images/**",
                                "/webjars/**",
                                "/swagger-ui.html",
                                "/webjars/**",
                                "/v2/api-docs",
                                "/configuration/ui",
                                "/configuration/security").permitAll()
                        // swagger end;
                        .antMatchers("/favicon.ico").permitAll()
                        .antMatchers("/actuator/**").permitAll()
                        .antMatchers("/guest/**").permitAll()
                        .antMatchers(HttpMethod.GET, "/version").permitAll()
                        .antMatchers("/auth/**","/oauth/remove_token", "/oauth/check_permission").authenticated();

        ignoreUrlsProperties.getUrls().forEach((url, methods) -> {
            if (StrUtil.isNotEmpty(methods)) {
                Arrays.asList(methods.split(",")).forEach(method -> {
                    if (StrUtil.isNotBlank(method)) {
                        if ("*".equals(method)) {
                            registry.antMatchers(url).permitAll();
                        } else {
                            registry.antMatchers(HttpMethod.valueOf(method.toUpperCase()), url).permitAll();
                        }
                    }
                });
            }
        });

        // springEL表达式方式动态url权限校验
        registry.anyRequest()
                .access("@permissionService.hasPermission(request,authentication)");
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore);
        resources.expressionHandler(expressionHandler);
        if (StrUtil.isNotBlank(oAuth2ClientProperties.getClientId())) {
            resources.resourceId(oAuth2ClientProperties.getClientId());
        }
        if (Objects.nonNull(remoteTokenServices)) {
            resources.tokenServices(remoteTokenServices);
        }
    }

    @Bean
    public OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler(ApplicationContext applicationContext) {
        OAuth2WebSecurityExpressionHandler expressionHandler = new OAuth2WebSecurityExpressionHandler();
        expressionHandler.setApplicationContext(applicationContext);
        return expressionHandler;
    }
}
